package com.pine.app.module.security.core.authorize.filter;

import com.pine.app.module.security.core.Authentication;
import com.pine.app.module.security.core.SecurityContextHolder;
import com.pine.app.module.security.core.authorize.decisions.AuthorizeDecisionHanler;
import com.pine.app.module.security.core.authorize.manager.MethodAuthorizeDataManager;
import com.pine.app.module.security.core.authorize.manager.impl.DefaultMethodAuthorizeDataManagerImpl;
import com.pine.app.module.security.core.authorize.metadataSource.ConfigAttribute;
import com.pine.app.module.security.core.authorize.metadataSource.FilterInvocationSecurityMetadataSource;
import com.pine.app.module.security.core.authorize.metadataSource.PermissionType;
import com.pine.app.module.security.core.common.enums.ErrorType;
import com.pine.app.module.security.core.exception.AccessDeniedException;
import com.pine.app.module.security.core.web.BaseWebFilter;
import com.pine.app.module.security.oauth.exception.AuthenticationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.util.CollectionUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 权限处理拦截器
 *
 * @author xiaoyuan
 * @create 2020/3/23 15:29
 **/
@Order(200)
public class SecurityAuthorizeFilter extends BaseWebFilter {


    private FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource;

    private AuthorizeDecisionHanler authorizeDecisionHanler;

    private final MethodAuthorizeDataManager methodAuthorizeDataManager = new DefaultMethodAuthorizeDataManagerImpl();

    public SecurityAuthorizeFilter() {

    }

    @Autowired
    public void setFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {
        this.filterInvocationSecurityMetadataSource = filterInvocationSecurityMetadataSource;
    }

    public SecurityAuthorizeFilter(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {
        this.filterInvocationSecurityMetadataSource = filterInvocationSecurityMetadataSource;

    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        Authentication authentication =  SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            ErrorType.AUTHENTICATION_ERROR.throwThis(AuthenticationException::new);
        }
        authorizeDecisionHanler = new AuthorizeDecisionHanler();
        List<ConfigAttribute> list = this.filterInvocationSecurityMetadataSource.getAttributes(httpServletRequest);
        if (!CollectionUtils.isEmpty(list)) {
            Map<PermissionType, List<ConfigAttribute>> map = list.stream().collect(Collectors.groupingBy(ConfigAttribute::getType));
            if(map!=null&&map.size()>0){
                Set<PermissionType> sets = map.keySet();
                if (!CollectionUtils.isEmpty(sets)) {
                    final boolean[] result = {true};
                    sets.forEach(permissionType -> {
                        result[0] = authorizeDecisionHanler.decide(permissionType.getCode(), map.get(permissionType), httpServletRequest);
                    });
                    if (!result[0]) {
                        ErrorType.ACCESS_DENIED.throwThis(AccessDeniedException::new);
                    }
                }
            }

        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}
